import asyncio
import websockets
import threading
import msgpack
import queue
import time
from datetime import datetime

class CEffect:
    def __init__(self, data):
        (
            self.m_name,
            self.m_obj,
        ) = data

class SAttackInfo:
    def __init__(self, data):
        (
            self.m_skillTargetID,
            self.m_skillName,
            self.m_skillChargeRemainingTime,
        ) = data

class CActor:
    def __init__(self, data):
        (
            self.m_obj,
            self.m_LocalPlayers ,
            self.m_x,
            self.m_y,
            self.m_z,
            self.m_vx,
            self.m_vy,
            self.m_vz,
            self.m_isLive,
            self.m_isAttack,
            self.m_isLocal,
            self.m_selectID,
            self.m_utf8name,
            self.m_utf8AreaName,
            self.m_hp,
            self.m_hpMax,
            self.m_mp,
            self.m_mpMax,
            self.m_id,
            self.m_distance,
            self.m_castID,
            self.m_isAnimation,
            self.m_isMove,
            self.m_weaponIndex,
            self.m_worldMessageCharacterId,
            self.m_level,
            self.m_targetID,
            self.m_stamina,#人物体能
            self.m_statPoints,#属性点
            self.m_strength,#斗志
            self.m_dexterity,#中气
            self.m_wisdom,#智慧
            self.m_perception,#洞察
            self.m_isDaytime,#是否是白天
            self.m_gold,#金币
            self.m_isPlayer,
            self.m_skillTargetID,#攻击信息
            self.m_skillName,
            self.m_skillChargeRemainingTime,
            self.m_abyssPoint , #深渊点数
            self.m_PartyDungeonPoint , #副本点数
            self.m_ContractPoint ,  #委托卷数量
            self.m_isDungeon ,
            m_Effects #效果数组

        ) = data
        self.m_Effects = []
        for v in m_Effects:
            self.m_Effects.append(CEffect(v))

class CQuickSlot:
    def __init__(self, data):
        (
            self.m_Name,    #技能名
            self.m_Cooling, #//冷却中
            self.m_index,   #快捷栏序号
            self.m_bright,     #快捷栏是否是明亮的状态
            self.m_CoolingTime,
            self.m_Resources ,
            self.m_Distance
        ) = data

class CItem:
    def __init__(self, data):
        (
            self.m_id,
            self.m_id2 ,
            self.m_category,  
            self.m_count,     
            self.m_name,
            self.m_level,
            self.m_colour,
            self.m_equipment,
        ) = data
        

class SExecution:
    def __init__(self, data):
        (
            self.Count,    
            self.MaxCount,
            self.Id,  
            self.IsComplete,     
            self.Describe
        ) = data     

class CTask:
    def __init__(self, data):
        (
            self.m_Name,    
            self.m_Id,
            ExecutionData
        ) = data     
        self.m_ExecutionList = ExecutionData

class STeleportPoint:
    def __init__(self, data):
        (
            self.name,    
            self.id,
            self.isOpen,  
        ) = data     

class CSkill:
    def __init__(self, data):
        (
            self.m_id1,    
            self.m_id2,
            self.m_level,  
            self.m_ResourceCost,  
            self.m_Distance,  
            self.m_IsCooldown,
            self.m_name

        ) = data   

# type:
# 和平 ： 1
# 合作：  3
# 公会 ： 2
# 阵营战： 0
# 纷争：  4
# 巨鸟：  5 
class CSchedule:
    def __init__(self, data):
        (
            self.name,    
            self.description,
            self.type,  
        ) = data    

# 初始化全局计数器和上次打印时间
print_count = 0
last_print_time = time.time()
class AFApi:
    def __init__(self, host='0.0.0.0', port=8888):
        self.host = host
        self.port = port
        self.request_id = 0
        self.server_thread = None
        self.client_socket = None
        self.game_state = {}  # 存储游戏状态
        self.pending_requests = {}
        self.timeout=3

        self.custom_handlers = {}  # 字典用于存储自定义处理函数
        self._register_handler(100, self._onSendRangeObject)
        self._register_handler(101, self._onRevcQuickSlot)
        self._register_handler(102, self._onRevcInventory)

        self.m_LocalPlayers = {}    #玩家
        self.global_objects = []    #周围对象
        self.global_QuickSlot = []  #技能栏数组
        self.global_Inventory = []  #背包数组
        self.global_TaskDelegation = [] #已接委托任务
        self.global_TaskAdventureExploration = [] #冒险，探查任务库
        self.global_LocalPlayersEffect = [] #本地玩家效果数组

    async def clean_data(self):
        self.m_LocalPlayers = {}
        self.global_objects = []
        self.global_QuickSlot = []


    async def handle_client(self, websocket, path):
        self.client_socket = websocket
        addr = websocket.remote_address
        await self.clean_data()
        print("客户端已成功连接，地址：{}".format(addr))

        try:
            async for message in websocket:
                self.data = msgpack.unpackb(message, raw=False)
                response_type, request_id, *response_params = self.data

                if request_id <= 0:
                    # 调用自定义处理函数
                    if response_type in self.custom_handlers:
                        await self.custom_handlers[response_type](response_params[0])
                    else:
                        print(f"_handle_messages warning: No handler registered for response type {response_type}")
                else:
                    # print(f"handle_client {request_id}")
                    if request_id in self.pending_requests:
                        promise = self.pending_requests.pop(request_id)
                        promise.set_result(response_params)


        except websockets.exceptions.ConnectionClosed as e:
            print(f"Connection closed: {e}")
        finally:
            self.client_socket = None

    async def _send_request(self, request_type, *params):
        if self.client_socket is None:
            raise Exception('_send_request 失败，客户端套接字未连接')

        promise = asyncio.Future()
        self.request_id += 1
        id = self.request_id
        self.pending_requests[self.request_id] = promise
        message = [request_type, id] + list(params)
        encoded_message = msgpack.packb(message, use_bin_type=True)
        await self.client_socket.send(encoded_message)

        # 等待响应，并设置超时时间
        try:
            state , *response_params = await asyncio.wait_for(promise, timeout=5)
            if state == 0:
                return response_params
            else:
                return None
        except asyncio.TimeoutError:
            print("请求超时")
            return None
        return None

    
        #----------------------------------------------------- 消息响应函数 -----------------------------------------------------
    async def _onSendRangeObject(self, response_params):
        global print_count, last_print_time  # 声明使用全局变量
        print_count += 1
        # 检查是否已经过去了一秒钟
        current_time = time.time()
        if current_time - last_print_time >= 1:
            # print(f"在过去一秒内打印了 {print_count} 次。 {len(self.global_objects)}")
            print_count = 0  # 重置计数器
            last_print_time = current_time  # 更新上次打印时间
       
        self.global_objects.clear()  # 清空全局对象列表
        try:
            for obj_data in response_params:
                actor = CActor(obj_data)
                if actor.m_LocalPlayers:
                    self.m_LocalPlayers = actor
                else:
                    self.global_objects.append(actor)
        except Exception as e:
            print(f"处理响应参数时发生错误: {e}")

    async def _onRevcQuickSlot(self, response_params):
        self.global_QuickSlot.clear()
        for obj_data in response_params:
            slot = CQuickSlot(obj_data)
            self.global_QuickSlot.append(slot)

    async def _onRevcInventory(self, response_params):
        self.global_Inventory.clear()
        for obj_data in response_params:
            item = CItem(obj_data)
            self.global_Inventory.append(item)
    
    def _register_handler(self, response_type, handler):
        self.custom_handlers[response_type] = handler   

    def start_server(self):
        self.server_thread = threading.Thread(target=self._run_server)
        self.server_thread.start()

    def _run_server(self):
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.loop)
        start_server = websockets.serve(self.handle_client, self.host, self.port)
        self.loop.run_until_complete(start_server)
        print("服务器已成功创建并启动，正在监听端口 {} ...".format(self.port))
        self.loop.run_forever()

    def stop_server(self):
        if self.client_socket:
            self.client_socket.close()

    #选择对象
    def API_SelectObj(self, objID):
        ret = asyncio.run_coroutine_threadsafe(self._send_request(10016 , objID), self.loop)
        return ret
    
    #传送
    def API_Teleport(self , id):
        future = asyncio.run_coroutine_threadsafe(self._send_request(10017 , id), self.loop)
        result = future.result()  # 这将阻塞，直到协程完成
    
    #取指定任务
    # type:0代表冒险和探查，1代表委托
    def API_GetTasks(self, type):
        future = asyncio.run_coroutine_threadsafe(self._send_request(10018 , type), self.loop)
        result = None
        try:
            result = future.result()  # 这将阻塞，直到协程完成
            if result == None:
                return None
        except Exception as e:
            print(f"API_GetTasks err: {e}")
        if type == 0:
            TaskAdventureExploration = []
            for obj_data in result[0]:
                item = CTask(obj_data)
                TaskAdventureExploration.append(item)
            return TaskAdventureExploration
        
        if type == 1:
            TaskDelegation = []
            for obj_data in result[0]:
                    item = CTask(obj_data)
                    TaskDelegation.append(item)
            return TaskDelegation
        
        return []
    
    #穿戴装备
    # id:装备id
    # index:几号套装栏
    def API_UseEquipment(self, id, index):
        future = asyncio.run_coroutine_threadsafe(self._send_request(10019, id, index), self.loop)
        result = future.result()  # 这将阻塞，直到协程完成

    #根据文本查找UI
    # text:UI的文本内容    
    # 返回：found (bool),x,y,width,height
    def API_FindSWidgetForText(self , text):
        future = asyncio.run_coroutine_threadsafe(self._send_request(10020, text), self.loop)
        result = future.result()  # 这将阻塞，直到协程完成
        return result
    
    # 取传送点数组  
    # 返回：STeleportPoint对象数组
    def API_GetTeleportPoint(self):
        future = asyncio.run_coroutine_threadsafe(self._send_request(10021), self.loop)
        response = future.result()  # 这将阻塞，直到协程完成
        ret = []
        for obj_data in response[0]:
            item = STeleportPoint(obj_data)
            ret.append(item)
        return ret
    
    # 取所有技能
    # 返回：CSkill 对象数组。。
    def API_GetAllSkills(self):
        future = asyncio.run_coroutine_threadsafe(self._send_request(10022), self.loop)
        response = future.result()  # 这将阻塞，直到协程完成
        ret = []
        for obj_data in response[0]:
            item = CSkill(obj_data)
            ret.append(item)
        return ret
    
    def API_UseConsumable(self, itemID, itemID2: int, destID: int, destX: float, destY: float, destZ: float):
        """
        使用消耗品的 API 接口。

        参数:
            itemID (int): 物品的 ID，类型为整数。
            itemID2 (int): 物品的 ID2，类型为整数。
            destID (int): 目标 ID，类型为整数。
            destX (float): 目标位置的 X 坐标，类型为浮点数。
            destY (float): 目标位置的 Y 坐标，类型为浮点数。
            destZ (float): 目标位置的 Z 坐标，类型为浮点数。

        返回:
            dict: 请求的响应，包含状态和数据。
        """
        future = asyncio.run_coroutine_threadsafe(self._send_request(10023, itemID, itemID2, destID, destX, destY, destZ), self.loop)
        response = future.result()  # 这将阻塞，直到协程完成
        return response
    
    # 使用任务奖励消耗品
    def API_UseConsumableWithQuantity(self ,itemID , sum):
        future = asyncio.run_coroutine_threadsafe(self._send_request(10024, itemID, sum), self.loop)
        response = future.result()  # 这将阻塞，直到协程完成
        return response

    # 获取正在进行日程表
    def API_GetOngoingSchedule(self):
        response = self._send_request(10025)
        ret = []
        for obj_data in response[0]:
            item = CSchedule(obj_data)
            ret.append(item)
        return ret
    
    # 交互
    # type:0为对话 1为钩锁
    def API_Interactive(self , id ):
        future = asyncio.run_coroutine_threadsafe(self._send_request(10026, id), self.loop)
        response = future.result()  # 这将阻塞，直到协程完成
        return response
    
    # 移动到对象
    # actor_id:周围对象id
    def API_MovToActor(self , actor_id ):
        future = asyncio.run_coroutine_threadsafe(self._send_request(10027, actor_id), self.loop)
        response = future.result()  # 这将阻塞，直到协程完成
        return response
    
    #获得背包数据
    def API_GetInventory(self):
        future = asyncio.run_coroutine_threadsafe(self._send_request(10029), self.loop)
        response = future.result()  # 这将阻塞，直到协程完成
        ret = []
        for obj_data in response[0]:
            item = CItem(obj_data)
            ret.append(item)
        return ret
    
    #朝向
    def API_FacingCoordinates(self, x , y ,z):
        future = asyncio.run_coroutine_threadsafe(self._send_request(10015, float(x) , float(y) ,float(z)), self.loop)
        response = future.result()  # 这将阻塞，直到协程完成
        return response
    
    # 剧情设置玩家名字
    def API_SetCharacterName(self , name ):
        future = asyncio.run_coroutine_threadsafe(self._send_request(10028, name), self.loop)
        response = future.result()  # 这将阻塞，直到协程完成
        return response
    
# # 启动服务器
# if __name__ == "__main__":
#     api = AFApi()
#     api.start_server()
#     try:
#         # 在这里可以运行其他代码，服务器将在后台运行
#         while True:
#             if api.client_socket:
#                 # time.sleep(0.1)
#                 # api.API_Teleport(0x15523809245ECAF8)
#                 # 获取当前时间
#                 now = datetime.now()

#                 # 转换为字符串格式
#                 formatted_time = now.strftime("%Y-%m-%d %H:%M:%S")

#                 # 打印时间
#                 print(formatted_time)
                
#                 ret = api.API_GetTasks(0) 
#                 time.sleep(1)
#                 pass

#     except KeyboardInterrupt:
#         print("Stopping server...")
#         api.stop_server()